#ifndef _CANARY2_SYS_FD_GUARD_H_
#define _CANARY2_SYS_FD_GUARD_H_

#include <utility>
#include <fcntl.h>
#include <cstring>
#include <stdexcept>
#include <cerrno>
#include <system_error>

namespace wilson::sys
{
    class fd_guard
    {
    public:
        constexpr fd_guard() = default;
        fd_guard(int fd);
        ~fd_guard() noexcept;

        fd_guard(fd_guard&&) noexcept;
        fd_guard& operator=(fd_guard&&) noexcept;

        fd_guard(const fd_guard&) = delete;
        fd_guard& operator=(const fd_guard&) = delete;

        operator int() const noexcept { return fdg_fd; }
        ::std::error_code error() const noexcept;
        constexpr bool empty() const noexcept { return fdg_fd == 0; }
        void close() noexcept;
        void set_nonblk() { this->fcntl(F_SETFL, O_NONBLOCK); }
        fd_guard dup() const noexcept;        

        template<typename... Args>
        int fcntl(int cmd, Args&&... args)
        {
            int ret = ::fcntl(fdg_fd, cmd, ::std::forward<Args>(args)...);
            if (ret == -1)
            {
                throw ::std::runtime_error{ ::strerror(errno) };
            }
            return ret;
       }

    private:
        int fdg_fd{};
    };

    class borrowed_fd
    {
    public:
        constexpr borrowed_fd() = default;

        constexpr borrowed_fd(int fd) 
            : bf_fd{ fd }
        {
        }

        borrowed_fd(const fd_guard& fd)
            : bf_fd{ int(fd) }
        {
        }

        int fd() const noexcept { return bf_fd; }

        operator int() const noexcept { return bf_fd; }

    private:
        int bf_fd;
    };
}

#endif
